from typing import Iterable, Union


class Matrix:
    def __init__(self, row: int, col: int, values: Iterable[float] = None):
        def generate_values():
            if values is not None:
                for value in values:
                    yield value
            while True:
                yield 0
        if col <= 0 or row <= 0:
            raise ValueError()
        generated = iter(generate_values())
        self._storage: list[list[float]] = \
            [[next(generated) for _ in range(col)] for _ in range(row)]

    # @staticmethod
    # def create_two_dimension(col: int, row: int, values: Iterable[Iterable[float]]):
    #    def generate_values():
    #        for r in values:
    #            for v in r:
    #                yield v
    #    return Matrix(col, row, generate_values())

    @property
    def row_count(self):
        return len(self._storage)

    @property
    def col_count(self):
        return len(self._storage[0])

    def set_value(self, i: int, j: int, value: float):
        self._storage[i][j] = value

    def get_value(self, i: int, j: int):
        return self._storage[i][j]

    def get_row(self, i: int):
        for value in self._storage[i]:
            yield value

    def get_rows(self):
        for i in range(self.row_count):
            yield self.get_row(i)

    def get_col(self, j: int):
        for i in range(self.row_count):
            yield self._storage[i][j]

    def get_cols(self):
        for j in range(self.col_count):
            yield self.get_col(j)

    def plain(self):
        for row in self._storage:
            for value in row:
                yield value

    def format(self, item_format: str = ".2f", item_sep: str = " ", line_sep: str = "\n"):
        def generate_strs(lst: list[float]):
            for v in lst:
                yield v.__format__(item_format)

        def generate_lines():
            for r in self._storage:
                yield item_sep.join(generate_strs(r))

        return line_sep.join(generate_lines())

    def __str__(self):
        return str(self._storage)

    def transposed(self):
        t = [self._storage[i][j] for j in range(self.col_count) for i in range(self.row_count)]
        return Matrix(row=self.col_count, col=self.row_count, values=t)

    def __mul__(self, other: Union['Matrix', float]):
        if isinstance(other, Matrix):
            if self.col_count != other.row_count:
                raise ValueError()

            def inner_product(vector1: Iterable[float], vector2: Iterable[float]):
                result = 0
                for v1, v2 in zip(vector1, vector2):
                    result += v1 * v2
                return result

            def get_rows_list(m: 'Matrix'):
                for r in m.get_rows():
                    yield list(r)

            t = [inner_product(row, col) for row in get_rows_list(self) for col in other.get_cols()]
            return Matrix(self.row_count, other.col_count, t)
        else:  # suppose float
            return Matrix(self.row_count, self.col_count, [value * other for value in self.plain()])
